#include"stdio.h"
#include"os_type.h"
#include"asm_utils.h"
#include"os_modules.h"
#include"stdarg.h"
#include"stdlib.h"

#define MAX_KEY_SIZE 6

KEY keys[MAX_KEY_SIZE];
const char* map[]={"up","down","home","end","enter","backspace"};
//区分大小写
uint32 mark_total[3*MAX_KEY_SIZE]={0xE0,0x48,0, 0xE0,0x50,0, 0xE0,0x4B,0, 0xE0,0x4d,0, 0x1C,0,0, 0x0E,0,0};

STDIO::STDIO()
{
    initialize();
}
void initKey(KEY& key,const char* name,uint32 mark_code[3])
{
    // memset(key.name,0,20);
    strcpy(key.name,name);
    for(int i=0;i<3;i++)key.mark_code[i]=mark_code[i];
}
void STDIO::initialize()
{
    screen = (uint8*)0xc00b8000;
    curRow=0;
    for(int i=0;i<25*80;i++)
    {
        buffer[2*i]=screen[2*i];
        buffer[2*i+1]=screen[2*i+1];
    }
    for(int i=0;i<3;i++) keyPressing[i]=0;
    for(int i=0;i<MAX_KEY_SIZE;i++)
        initKey(keys[i],map[i],&mark_total[i*3]);
}

void STDIO::print(uint x,uint y,uint8 c,uint8 color)
{
    if(x >= 25 || y >= 80)
    {
        return;
    }
    uint pos = x*80+y;
    screen[2*pos]=c;
    screen[2*pos+1]=color;
}
void STDIO::print(uint8 c,uint8 color)
{
    uint cursor=getCursor();
    screen[2*cursor]=c;
    screen[2*cursor+1]=color;
    cursor++;
    if (cursor==25*80)
    {
        rollUp();
        cursor=24*80;
    }
    moveCursor(cursor);
}
void STDIO::print(uint8 c)
{
    print(c,0x07);
}
int STDIO::print(const char* const str)
{
    int i=0;
    for(i=0;str[i];i++)
    {
        uint row=0;
        switch(str[i])
        {
            case '\n':
                row=getCursor()/80;
                if(row==24)
                {
                    rollUp();
                }else{
                    row++;
                }
                moveCursor(row*80);
                break;
            default:
                print(str[i],0x05);
                break;
        }
    }
    return i;
}
void STDIO::moveCursor(uint position)
{
    if(position>=80*25)
    {
        return;
    }
    uint8 temp;
    //处理高8位
    temp=(position>>8)&0xff;
    asm_out_port(0x3d4,0x0e);
    asm_out_port(0x3d5,temp);
    //处理低8位
    temp=position & 0xff;
    asm_out_port(0x3d4,0x0f);
    asm_out_port(0x3d5,temp);
}
void STDIO::moveCursor(uint x,uint y)
{
    if(x>=25 || y>=80)
    {
        return;
    }
    moveCursor(x*80+y);
}
uint STDIO::getCursor()
{
    uint pos=0;
    uint8 temp=0;
    //处理高8位
    asm_out_port(0x3d4,0x0e);
    asm_in_port(0x3d5,&temp);
    pos=((uint)temp)<<8;
    //处理低8位
    asm_out_port(0x3d4,0x0f);
    asm_in_port(0x3d5,&temp);
    pos=pos | ((uint)temp);
    return pos;
}
void STDIO::rollUp()
{
    //最后一页，需要滚屏缓冲区，25为页面大小(25*80)
    if(curRow+25==MAX_BUFFER_SIZE)
    {
        curRow--;
        for(uint i=80;i<80*MAX_BUFFER_SIZE;i++)
        {
            buffer[2*(i-80)]=buffer[2*i];
            buffer[2*(i-80)+1]=buffer[2*i+1];
        }
        for(uint i=80*(MAX_BUFFER_SIZE-1);i<80*MAX_BUFFER_SIZE;i++)
        {
            buffer[2*i]=' ';
            buffer[2*i+1]=0x07;
        }
    }
    uint length=25*80;
    //复制到缓冲区
    for(uint i=0;i<length;i++)
    {
        buffer[2*(curRow*80+i)]=screen[2*i];
        buffer[2*(curRow*80+i)+1]=screen[2*i+1];
    }
    curRow++;
    //上滑
    for(uint i=80; i<length; i++)
    {
        screen[2*(i-80)]=screen[2*i];
        screen[2*(i-80)+1]=screen[2*i+1];
    }
    //最后一行
    for(uint i=24*80; i<length;i++)
    {
        screen[2*i]=buffer[2*(curRow*80+i)];
        screen[2*i+1]=buffer[2*(curRow*80+i)+1];
    }
}
void STDIO::rollDown()
{
    if(curRow==0)return;
    uint length=25*80;
    //复制到缓冲区
    for(uint i=0;i<length;i++)
    {
        buffer[2*(curRow*80+i)]=screen[2*i];
        buffer[2*(curRow*80+i)+1]=screen[2*i+1];
    }
    curRow--;
    //下滑,从后向前
    for(uint i=length-1; i>=80; i--)
    {
        screen[2*i]=screen[2*(i-80)];
        screen[2*i+1]=screen[2*(i-80)+1];
    }
    //第一行
    for(uint i=0; i<80;i++)
    {
        screen[2*i]=buffer[2*(curRow*80+i)];
        screen[2*i+1]=buffer[2*(curRow*80+i)+1];
    }
}
void STDIO::clear(uint8 color)
{
    for(uint32 i=0;i<25*80;i++)
    {
        screen[2*i]=' ';
        screen[2*i+1]=color;
    }
    curRow=0;
    for(uint32 i=0;i<80*MAX_BUFFER_SIZE;i++)
    {
        buffer[2*i]=' ';
        buffer[2*i+1]=color;
    }
    moveCursor(0);
}
void STDIO::handleKeyboard(uint32 scan_code)
{
    // printf(" handle%x ",scan_code);
    switch(keyPressing[0])
    {
        case 0xE1:
            if(keyPressing[1]==0)
            {
                keyPressing[1]=scan_code;
            }else if(keyPressing[2]==0){
                keyPressing[2]=scan_code;
            }else{
                for(int i=0;i<3;i++) keyPressing[i]=0;
                keyPressing[0]=scan_code;
            }
            break;
        case 0xE0:
            if(keyPressing[1]==0)
            {
                keyPressing[1]=scan_code;
            }else{
                for(int i=0;i<3;i++) keyPressing[i]=0;
                keyPressing[0]=scan_code;
            }
            break;
        default:
            if(keyPressing[0]==0)
            {
                keyPressing[0]=scan_code;
            }else{
                for(int i=0;i<3;i++) keyPressing[i]=0;
                keyPressing[0]=scan_code;
            }
    }
    // for(int i=0;i<3;i++)printf(" %x ",keyPressing[i]);
    handleKey();
}
void STDIO::handleKey()
{
    KEY* findKey=nullptr;
    for(int i=0;i<MAX_KEY_SIZE;i++)
    {
        bool find=true;
        for(int j=0;j<3;j++)
        {
            if(keys[i].mark_code[j]!=keyPressing[j])
            {
                find=false;
                break;
            }
        }
        if(find)
        {
            findKey=&keys[i];
            break;
        }
    }
    const char* str=findKey->name;
    uint pos=getCursor();
    uint row = pos/80;
    uint col = pos-row*80;
    if(strcmp(str,"")==0)return;
    else if(strcmp(str,"up")==0){
        if(row==0)rollDown();
        else moveCursor(row-1,col);
    }else if(strcmp(str,"down")==0){
        if(row==24)rollUp();
        else moveCursor(row+1,col);
    }else if(strcmp(str,"home")==0){
        moveCursor(row,col-1);
    }else if(strcmp(str,"end")==0){
        moveCursor(row,col+1);
    }else if(strcmp(str,"enter")==0){
        printf("\n");
    }else if(strcmp(str,"backspace")==0){
        if(col==0){
            if(row==0){
                if(curRow!=0)rollDown();
                else return;
            }else row--;
            col = 79;
        }else col--;
        screen[2*(row*80+col)]=' ';
        screen[2*(row*80+col)+1]=0x07;
        moveCursor(row,col);
    }
}
int printf_add_to_buffer(char* buffer,char c,int& index,const int BUF_LEN)
{
    int counter=0;
    buffer[index++]=c;
    if(index==BUF_LEN)
    {
        //输出字符串返回字符数量
        buffer[index]='\0';
        counter=stdio.print(buffer);
        index=0;
    }
    return counter;
}
int printf(const char* const format,...)
{
    const int BUF_LEN=32;
    char buffer[BUF_LEN+1];
    char number[33];//遇到输出数字存储在这里

    int index=0,counter=0;
    va_list ap;
    va_start(ap,format);
    //输出字符串
    for(int i=0; format[i]; i++)
    {
        if(format[i]!='%')
        {
            counter += printf_add_to_buffer(buffer,format[i],index,BUF_LEN);
        }else{
            i++;
            if(format[i]=='\0')break;
            switch(format[i])
            {
                case '%':
                    counter += printf_add_to_buffer(buffer,format[i],index,BUF_LEN);
                    break;
                case 'c':
                    counter += printf_add_to_buffer(buffer,va_arg(ap,int),index,BUF_LEN);
                    break;
                case 's':
                    buffer[index] = '\0';
                    index = 0;
                    counter += stdio.print(buffer);
                    counter += stdio.print(va_arg(ap,const char*));
                    break;
                case 'd':
                case 'x':
                    int temp=va_arg(ap,int);
                    if(temp<0 && format[i]=='d')
                    {
                        counter += printf_add_to_buffer(buffer,'-',index,BUF_LEN);
                        temp=-temp;
                    }
                    itos(number,temp,(format[i]=='d'?10:16));
                    for(int j=0;number[j];j++)
                    {
                        counter += printf_add_to_buffer(buffer,number[j],index,BUF_LEN);
                    }
                    break;
            }
        }
    }
    //清空缓冲区
    buffer[index]='\0';
    counter += stdio.print(buffer);
    va_end(ap);
    return counter;
}